import {
  downloadFile as downloadFileApi,
  uploadMultipleFiles as uploadMultipleFilesApi,
  uploadFile as uploadFileApi,
  downloadFileByUrl as downloadFileByUrlApi,
  uploadImageCompressionApi,
  uploadFilePublic,
  uploadFileBySharing
} from '@/apis/file';
import { FileMetaType } from '@/types/file';
import {
  OnTransferProgressType,
  TransferFileOptions,
  UploadImageOptions
} from '@/types/file';
import { AxiosProgressEvent, AxiosResponse } from 'axios';

function getTransferFileConfig(
  type: 'upload' | 'download',
  options?: TransferFileOptions | OnTransferProgressType
) {
  const { onProgress, signal, ...rest } =
    typeof options === 'function'
      ? ({ onProgress: options } as TransferFileOptions)
      : options || {};

  return onProgress || signal
    ? {
        ...rest,
        [type === 'download' ? 'onDownloadProgress' : 'onUploadProgress']: onProgress
          ? (e: AxiosProgressEvent) => {
              onProgress(
                e.progress !== undefined ? Math.floor(e.progress * 100) : -1,
                e.loaded,
                e.total
              );
            }
          : undefined,
        signal
      }
    : Object.keys(rest).length > 0
      ? rest
      : undefined;
}

export function downloadFile(
  input:
    | string
    | { filename: string; type?: string; content: string | Blob }
    | (() => Promise<AxiosResponse>),
  options?: TransferFileOptions | OnTransferProgressType,
  params?: Record<string, any>
) {
  if (typeof input === 'object') {
    const { filename, type, content } = input;
    const blob =
      typeof content === 'string'
        ? new Blob([`\uFEFF${content}`], { type: `${type || 'text/plain'};charset=utf-8;` })
        : content;
    const a = document.createElement('a');
    a.href = window.URL.createObjectURL(blob);
    a.download = filename;
    a.click();
    window.URL.revokeObjectURL(a.href);
    return Promise.resolve();
  }

  let req: Promise<AxiosResponse>;
  if (typeof input === 'string') {
    const config = getTransferFileConfig('download', options);
    if (input.startsWith('/')) {
      console.log(input);
      req = downloadFileByUrlApi(input, config, params);
    } else {
      req = downloadFileApi(input, config);
    }
  } else {
    req = input();
  }

  return req.then((res) => {
    const a = document.createElement('a');
    a.download = decodeURI(res.headers['content-disposition'].split(';')[1].split('=')[1]).replace(
      /"/g,
      ''
    );
    a.style.display = 'none';
    a.href = window.URL.createObjectURL(res.data);
    a.click();
  });
}

export function uploadFile(
  file: File,
  options?: TransferFileOptions | OnTransferProgressType,
  isPublic?: boolean
): Promise<FileMetaType>;

export function uploadFile(
  file: File[],
  options?: TransferFileOptions | OnTransferProgressType,
  isPublic?: boolean
): Promise<FileMetaType[]>;

export function uploadFile(
  input: File | File[],
  options?: TransferFileOptions | OnTransferProgressType,
  isPublic?: boolean
) {
  const config = getTransferFileConfig('upload', options);

  if (Array.isArray(input)) {
    const data = new FormData();
    input.forEach((it) => data.append('files', it, it.name));
    return isPublic ? uploadFilePublic(data, config) : uploadMultipleFilesApi(data, config);
  }

  const data = new FormData();
  data.append('file', input, input.name);
  return isPublic ? uploadFilePublic(data, config) : uploadFileApi(data, config);
}

export function uploadFileV3bySharing(
  input: File | File[],
  options?: TransferFileOptions | OnTransferProgressType
) {
  const config = getTransferFileConfig('upload', options);
  const data = new FormData();
  if (Array.isArray(input)) {
    input.forEach((it) => data.append('files', it, it.name));
  } else {
    data.append('file', input, input.name);
  }
  return uploadFileBySharing(data, config);
}

export function uploadFileV2(
  file: File,
  compositionRuleId: string,
  options?: TransferFileOptions | OnTransferProgressType,
  isPublic?: boolean
) {
  const config = getTransferFileConfig('upload', options);

  const data = new FormData();
  data.append('file', file, file.name);
  data.append('compositionRuleId', compositionRuleId);
  return isPublic ? uploadFilePublic(data, config) : uploadFileApi(data, config);
}

export function uploadImageWithCompression(
  file: File,
  options?: TransferFileOptions | OnTransferProgressType
): Promise<FileMetaType>;

export function uploadImageWithCompression(
  input: File,
  options?: TransferFileOptions | OnTransferProgressType
) {
  const config = getTransferFileConfig('upload', options);

  const data = new FormData();
  data.append('file', input, input.name);
  return uploadImageCompressionApi(data, config);
}

export function uploadImage(
  file: File,
  options?: UploadImageOptions | OnTransferProgressType,
  apiCompression?: boolean
): Promise<FileMetaType>;

export function uploadImage(
  files: File[],
  options?: UploadImageOptions | OnTransferProgressType,
  apiCompression?: boolean
): Promise<FileMetaType[]>;

export async function uploadImage(
  input: File | File[],
  options?: UploadImageOptions | OnTransferProgressType,
  apiCompression?: boolean
) {
  const { onProgress, maxSizeMB, maxWidthOrHeight, signal, isPublic } =
    typeof options === 'function' ? ({ onProgress: options } as UploadImageOptions) : options || {};

  const compressOptions =
    maxSizeMB || maxWidthOrHeight || signal ? { maxSizeMB, maxWidthOrHeight, signal } : undefined;

  const TransferFileOptions = onProgress || signal ? { onProgress, signal } : undefined;

  if (apiCompression && !Array.isArray(input)) {
    return uploadImageWithCompression(input, options);
  }

  if (Array.isArray(input)) {
    if (!compressOptions) {
      return uploadFile(input, TransferFileOptions, isPublic);
    }
    return uploadFile(input, TransferFileOptions, isPublic);
  }

  if (!compressOptions) {
    return uploadFile(input, TransferFileOptions, isPublic);
  }

  return uploadFile(input, TransferFileOptions, isPublic);
}
